﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Mongaba.Core.ServiceContracts;
using Mongaba.Core.Enumerators;
using Mongaba.Core.DataContracts;
using System.ServiceModel;
using System.Net;
using Mongaba.Core.Factory;

namespace Mongaba.Core
{
    internal delegate void MessageContractReceivedHandle(MessageContract m);
    internal delegate bool CoordinationReceivedHandle();
    internal delegate void ProcessListReceivedHandle(ProcessContract[] ipl);
    internal delegate bool BeginReceiveFileRequestHandle(string fileName,string operationGuid, long fileBytes, ProcessContract pContract);
    internal delegate bool ReceiveFilePartRequestHandle(string operationGuid, byte[] data, int offset, int count, ProcessContract pContract);
    internal delegate void CancelReceiveFileHandle(string operationGuid, ProcessContract p);
    internal delegate void EndReceiveFileHandle(string operationGuid, ProcessContract p);
    internal delegate void ReceiveNewCoordinatorHandle(string coordinatorAddress);
    [ServiceBehavior(Name = "MongabaProcess", InstanceContextMode = InstanceContextMode.Single, ConcurrencyMode = ConcurrencyMode.Multiple)]
    internal class MongabaProcess : IMongabaProcess
    {
        #region fields
        string _id = null;
        DateTime _connectionTime;
        ProcessStatus _status = ProcessStatus.Normal;
        UserContract _processUser = null;
        GroupContract _processGroup = null;
        ProcessScore _score = null;
        bool _isCoordinator;

        #endregion

        #region IProcess Members

        public string getId()
        {
            return _id;
        }

        public DateTime getConnectionTime()
        {
            return _connectionTime;
        }

        public void ReceiveMessage(MessageContract m)
        {

            try
            {
                OnMessageContractReceived(m);
            }
            catch
            {

            }
        }
        public MongabaProcess(UserContract user, GroupContract group)
        {
            this._connectionTime = DateTime.Now;
            this._id = Guid.NewGuid().ToString();
            _processUser = user;
            _processGroup = group;
            _score = new ProcessScore();
        }

        public bool ReceiveCoordination()
        {
            try
            {
                Console.WriteLine("Recebimento de coordenação requisitado");
                if (CoordinatorFactory.IsStarted)
                {
                    Console.WriteLine("Coordenação já iniciada e aceita.");

                    return true;
                    
                }
                else
                {                    

                    CoordinatorFactory.RemoveRemoteInstanceConnection(CoordinatorFactory.CurrentContract.Address);
                    CoordinatorFactory.StartLocalInstance(this.getProcessGroup());
            
                    ReceiveNewCoordinator(CoordinatorFactory.CurrentContract.Address);
                    Console.WriteLine("Coordenação iniciada local.");
                    return true;
                   
                }

               
            }
            catch (Exception ex)
            {
                Console.WriteLine("Erro erro no recebimento da coordenação. Erro: " + ex.Message + "Stack Trace: " + ex.StackTrace);                
                return false;
            }
        }

        public ProcessStatus getStatus()
        {
            return _status;
        }

        public UserContract getProcessUser()
        {
            return _processUser;
        }

        public GroupContract getProcessGroup()
        {
            return _processGroup;
        }

        public void ReveiveProcessList(ProcessContract[] plist)
        {
            try
            {
                OnProcessListReceived(plist);               
            }
            catch (Exception ex)
            {
                Console.WriteLine("Erro no recebimento da lista de processos: " +ex.Message);

            }
        }

        public void RenewScore()
        {
            _score.refreshScore();
        }

        public void setScore(ProcessScore score)
        {
            _score = score;
        }
        public ProcessContract getContract()
        {
            ProcessContract p = new ProcessContract();
            p.Address = Utilities.HostIp.ToString();
            p.Id = _id;
            p.IsCoordinator = _isCoordinator;
            p.Score = _score.getScore();
            p.Status = _status;
            p.User = _processUser;

            return p;
        }

        public float getScore()
        {
            return _score.getScore();
        }

        public bool Ping()
        {
            return true;
        }

        #endregion

        public event MessageContractReceivedHandle OnMessageContractReceived;
        public event CoordinationReceivedHandle OnReceiveCoordination;
        public event ProcessListReceivedHandle OnProcessListReceived;
        public event BeginReceiveFileRequestHandle OnBeginReceiveFile;
        public event ReceiveFilePartRequestHandle OnReceiveFilePart;
        public event CancelReceiveFileHandle OnCancelReceiveFile;
        public event EndReceiveFileHandle OnEndReceiveFile;
        public event ReceiveNewCoordinatorHandle OnReceiveNewCoordinator;
        #region IMongabaProcess Members


        public bool BeginReceiveFile(string fileName,string operationGuid, long fileBytes, ProcessContract pContract)
        {            
            Console.WriteLine(String.Format("Requisição de recebimento de arquivo do usuário {0}.",pContract.User.Name));
            return OnBeginReceiveFile(fileName, operationGuid,fileBytes, pContract);
        }

        public bool ReceiveFilePart(string operationGuid, byte[] data, int offset, int count, ProcessContract pContract)
        {
            foreach (Delegate d in OnReceiveFilePart.GetInvocationList())
            {
                try
                {
                    if (((ReceiveFilePartRequestHandle)d).Invoke(operationGuid, data, offset, count, pContract))
                    {
                        return true;
                    }
                }
                catch (Exception ex)
                {
                    Console.WriteLine("Erro no recebimento de arquivo. " + ex.Message);
                    OnReceiveFilePart -= ((ReceiveFilePartRequestHandle)d);
                }

            }

            return false;
        }

        public void CancelReceiveFile(string operationGuid, ProcessContract pContract)
        {
            OnCancelReceiveFile(operationGuid, pContract);
            Console.WriteLine(String.Format("Requisição de cancelamento de recebimento de arquivo do usuário {0}.", pContract.User.Name));
        }

        public void EndReceiveFile(string operationGuid, ProcessContract pContract)
        {
            OnEndReceiveFile(operationGuid, pContract);
        }

        public void ReceiveNewCoordinator(string coordAddress)
        {
            Console.WriteLine("Requisição de recebimento de nova coordenação.");
            OnReceiveNewCoordinator(coordAddress);            
        }

        #endregion

    }
}
